/**
 * 
 */
package uk.ac.manchester.cs.graph;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>Title: Node</p>
 * 
 * <p>Description: 
 * </p>
 * 
 * <p>Copyright: Copyright (c) 2007, 2008</p>
 * 
 * <p>Company: Clark & Parsia, LLC. <http://www.clarkparsia.com></p>
 * 
 * @author pavel
 */
public class Node<T> {

	private T				m_object;
	private List<Node<T>>	m_children;
	private Node<T>			m_parent;

	public Node(Node<T> parent) {

		m_parent = parent;
	}

	public Node(Node<T> parent, T object) {

		this( parent );
		setObject( object );
	}

	public Node<T> getParent() {

		return m_parent;
	}

	protected void setParent(Node<T> parent) {

		m_parent = parent;
	}

	public List<Node<T>> getChildren() {

		if( this.m_children == null ) {

			m_children = new ArrayList<Node<T>>();
		}

		return m_children;
	}

	public void setChildren(List<Node<T>> children) {

		this.m_children = children;
	}

	public int getNumberOfChildren() {

		if( m_children == null ) {
			return 0;
		}
		return m_children.size();
	}

	public boolean isLeaf() {

		return getNumberOfChildren() == 0;
	}

	public boolean isRoot() {

		return null == m_parent;
	}

	public Node<T> addChild(T child) {

		Node<T> childNode = newNode( this );

		childNode.setObject( child );

		if( m_children == null ) {
			m_children = new ArrayList<Node<T>>();
		}

		m_children.add( childNode );
		
		return childNode;
	}

	public void setChildAt(Node<T> child, int index) {

		if( null != child && null != m_children && index < m_children.size() ) {
			child.setParent( this );
			m_children.set( index, child );
		}
	}
	
	public void removeChildren() {
		
		m_children.clear();
	}

	public Node<T> getChildAt(int index) {

		return (null != m_children && index < m_children.size())
			? m_children.get( index )
			: null;
	}

	protected void copyFrom(Node<T> node) {

		List<Node<T>> children = node.getChildren();

		if( null != children ) {

			m_children = new ArrayList<Node<T>>( children.size() );

			for( Node<T> child : children ) {

				m_children.add( child.cloneNode( this ) );
			}
		}
		// Might need to create a deep copy of the object as well
		copyObject(node);
	}

	public T getObject() {

		return this.m_object;

	}

	public void setObject(T data) {

		this.m_object = data;

	}

	public String toString() {

		StringBuilder sb = new StringBuilder();
		sb.append( "{" ).append( getObject().toString() ).append( ",[" );
		int i = 0;

		for( Node<T> e : getChildren() ) {
			if( i > 0 ) {
				sb.append( "," );
			}
			sb.append( e.getObject().toString() );
			i++;
		}
		sb.append( "]" ).append( "}" );

		return sb.toString();
	}

    protected void output(StringBuffer sb, int level) {
    	
    	sb.append( System.getProperty("line.separator") );
    	
    	for (int i = 0; i < level; i++) {
    		sb.append( "\t" );
    	}
    	
    	sb.append( m_object.toString() );
    	
    	for (Node<T> child : getChildren()) {
    		
    		child.output(sb, level + 1);
    	}
    }
	
	
	public Node<T> cloneNode(Node<T> parent) {

		Node<T> node = newNode( parent );

		node.copyFrom( this );

		return node;
	}

	protected Node<T> newNode(Node<T> parent) {

		return new Node<T>( parent );
	}
	
	/*
	 * Dead nodes are terminal ones. Standard walking algorithm visits neither
	 * them nor their descendants 
	 */
	protected boolean isDead() {
		
		return null == m_object;
	}
	
	protected void copyObject(Node<T> node) {
		
		m_object = node.getObject();
	}
}